home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / winterp-1.13 / src-server / w_pixmap.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-10-04  |  16.1 KB  |  359 lines

  1. /* -*-C-*-
  2. ********************************************************************************
  3. *
  4. * File:         w_pixmap.c
  5. * RCS:          $Header: w_pixmap.c,v 1.3 91/03/14 03:13:50 mayer Exp $
  6. * Description:  Interfaces to Motif's Pixmap/XImage routines.
  7. * Author:       Niels Mayer, HPLabs
  8. * Created:      Thu Sep 28 18:59:42 1989
  9. * Modified:     Thu Oct  3 20:56:05 1991 (Niels Mayer) mayer@hplnpm
  10. * Language:     C
  11. * Package:      N/A
  12. * Status:       X11r5 contrib tape release
  13. *
  14. * WINTERP Copyright 1989, 1990, 1991 Hewlett-Packard Company (by Niels Mayer).
  15. * XLISP version 2.1, Copyright (c) 1989, by David Betz.
  16. *
  17. * Permission to use, copy, modify, distribute, and sell this software and its
  18. * documentation for any purpose is hereby granted without fee, provided that
  19. * the above copyright notice appear in all copies and that both that
  20. * copyright notice and this permission notice appear in supporting
  21. * documentation, and that the name of Hewlett-Packard and David Betz not be
  22. * used in advertising or publicity pertaining to distribution of the software
  23. * without specific, written prior permission.  Hewlett-Packard and David Betz
  24. * make no representations about the suitability of this software for any
  25. * purpose. It is provided "as is" without express or implied warranty.
  26. *
  27. * HEWLETT-PACKARD AND DAVID BETZ DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
  28. * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
  29. * IN NO EVENT SHALL HEWLETT-PACKARD NOR DAVID BETZ BE LIABLE FOR ANY SPECIAL,
  30. * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  31. * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  32. * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  33. * PERFORMANCE OF THIS SOFTWARE.
  34. *
  35. * See ./winterp/COPYRIGHT for information on contacting the authors.
  36. * Please send modifications, improvements and bugfixes to mayer@hplabs.hp.com
  37. * Post XLISP-specific questions/information to the newsgroup comp.lang.lisp.x
  38. **
  39. ********************************************************************************
  40. */
  41. static char rcs_identity[] = "@(#)$Header: w_pixmap.c,v 1.3 91/03/14 03:13:50 mayer Exp $";
  42.  
  43. #include <stdio.h>
  44. #include <Xm/Xm.h>
  45. #include "winterp.h"
  46. #include "user_prefs.h"
  47. #include "xlisp/xlisp.h"
  48.  
  49. /* 
  50.  
  51. Destroying unused Pixmaps and XImage's:
  52.  
  53. Pixmaps are used by widgets to display graphical information. By keeping
  54. track of the widget and the resource under which the pixmap is stored, we
  55. can figure out when to free up pixmaps by using XmDestroyPixmap() to update
  56. the reference count on pixmaps used in winterp and free the memory for
  57. pixmaps that are no longer referenced. This is certainly useful and good to
  58. do because I expect pixmaps to take up a reasonable amount of client and
  59. server space, so we should be efficient about their use, especially because
  60. motif has made it easy to do so. 
  61.  
  62. Furthermore, we don't want users manually calling XmDestroyPixmap() on a
  63. pixmap that is still referenced -- this will cause a guaranteed coredump
  64. due to an "X Protocol Error". 
  65.  
  66. Unfortunately, the XImage* behind the pixmap still seems to persist even if
  67. all pixmaps based on a particular Ximage no longer exists. Since XImages
  68. can be created inside XmGetPixmap(), or through a XmSttring-->Xm*Pixmap*
  69. conversion it will be very hard to get a hold of the actual XImage pointer
  70. when all we have access to are the name-strings of the previously installed
  71. images or the filenames of new images installed. Either we'll have to
  72. prevent creating cached XImages inside XmGetPixmap() and the
  73. XmSttring-->Xm*Pixmap* converters, and do all this ourselves, or we can
  74. just punt. I'm going to punt on this.
  75.  
  76. The alternative for handling XImages is to let the users that care about
  77. space taken up by XImages to use
  78. XmInstallImage(_XmGetImageFromFile(<image_file_name>)) at application
  79. startup time, and then destroy the <XImage_obj> themselves when they're not
  80. needed.  Destroying an XImage after creating the necessary pixmaps from it
  81. shouldn't cause any protocol errors, so this is the thing to do.
  82.  
  83. Every time a Pixmap is accessed either through XmGetPixmap() or through
  84. winterp's string-->pixmap converter (which ends up calling XmGetPixmap()),
  85. the reference count on that pixmap is automatically increased. We want to
  86. symetrically reduce the reference count (by calling XmDestroyPixmap()) for
  87. each pixmap reference that is no longer used. When the reference count is
  88. reduced to 0, the pixmap itself is freed. We do this by creating an atomic
  89. type XLTYPE_Pixmap to store the X Pixmap pointer; every reference to this
  90. pixmap will create a pixmap reference object which points to this
  91. XLTYPE_Pixmap object. Code in this module will keep track of the pixmap
  92. references made, and will remove any pixmap-ref-objs from v_savedobjs when
  93. the pixmap is no longer referenced. If no more references to the
  94. XLTYPE_Pixmap exist, then it will be garbage collected -- code inside the
  95. garbage collector will call XmDestroyPixmap() on these unused pixmaps.
  96.  
  97. In each pixmap-reference object, we store
  98. 1) a pointer to an object of type LVAL_Pixmap which is returned by
  99.    primitive xm_get_pixmap.
  100. 2) name of widget resource storing the pixmap (the winterp symbol for the res)
  101. 3) the widget-object of the pixmap.
  102.  
  103. We remove pixmap-ref-objs from v_savedobjs when
  104. 1) the widget which references the pixmap gets destroyed
  105. 2) the widget resource that references the pixmap gets set to a new value
  106. 3) the pixmap is no longer referenced within the lisp system. (see note below)
  107.  
  108. In case (1) we want to hash the pixmap into the widget's v_savedobjs
  109. hashbucket. When the widget's destroy callback gets called, all the objects
  110. in the hashbucket corresonding to that widget are destroyed (callbacks,
  111. pixmaps, etc).
  112.  
  113. In case (2), since we use the scheme mentioned above in (1) to store
  114. pixmap-objs, we can just search thru the hashbucket looking for the
  115. pixmap-obj on the approprate widget and widget-resource symbol. If we
  116. find a pixmap-obj, remove it from the list and replace it with the new one.
  117.  
  118. In case (3), when no pixmap-ref-objs pointing to a particular XLTYPE_Pixmap
  119. exist, that XLTYPE_Pixmap will get garbage collected and it's pixmap will
  120. be deallocated by calling XmDestroyPixmap(). Note that if XmGetPixmap is
  121. called N times on a particular pixmap, we create N XLTYPE_Pixmap objects
  122. corresponding to the N refcount on that Pixmap stored inside Motif. Each
  123. time the XLTYPE_Pixmap gets gc'd, the refcount gets reduced inside Motif.
  124.  
  125. The only other way a pixmap can be referenced is via XmGetPixmap() or
  126. :get_values on a pixmap resource, in which case it's XmDestroyPimap()
  127. operation should only happen when that pixmap was no longer referenced
  128. neither in lisp variables nor as pixmap resources. To do this, we create a
  129. new XLTYPE_Pixmap for each reference.
  130.  
  131. */
  132.  
  133. /******************************************************************************
  134.  *
  135.  ******************************************************************************/
  136. void Wpm_Set_Pixmap_Reference(lval_pixmap, o_widget, lval_resname)
  137.      LVAL lval_pixmap;        /* XLTYPE_Pixmap */
  138.      LVAL o_widget;        /* widget on which this resource got set */
  139.      LVAL lval_resname;        /* SYMBOL */
  140. {
  141.   extern LVAL v_savedobjs;    /* w_savedobjs.c */
  142.   int i = Wso_Hash(o_widget);    /* note that we hash all pixmapobjs on the same widget to the same hashbucket */
  143.   LVAL l_hbucket = getelement(v_savedobjs, i);
  144.   LVAL obj, refobj, l_prev = NIL;
  145.  
  146. #ifdef DEBUG
  147.   fprintf(stderr, "\nWpm_Set_Pixmap_Reference(PixmapID=%lu, WidgetID=%lu, ResnameID=%lu).\n", (unsigned long) get_pixmap(lval_pixmap), (unsigned long) o_widget, (unsigned long) lval_resname);
  148. #endif
  149.  
  150.   /* go thru hashbucket, stopping if hit end, or if hit match */
  151.   while (l_hbucket
  152.      && !((obj = car(l_hbucket))
  153.           && (ntype(obj) == XLTYPE_PIXMAP_REFOBJ)
  154.           && (get_pixref_widget(obj) == o_widget)
  155.           && (get_pixref_resname(obj) == lval_resname))) {
  156.      l_prev = l_hbucket;
  157.      l_hbucket = cdr(l_hbucket);
  158.        }
  159.  
  160.   /* remove the previously referenced pixmapobj --> allow it to be gc'd */
  161.   if (l_hbucket) {        /* if something matched */
  162. #ifdef DEBUG
  163.     fprintf(stderr, "    Removing PixmapRef w/ PixmapID=%lu, WidgetID=%lu, ResnameID=%lu.\n", (unsigned long) get_pixmap(get_pixref_pixmap(car(l_hbucket))), (unsigned long) get_pixref_widget(car(l_hbucket)), (unsigned long) get_pixref_resname(car(l_hbucket)));
  164. #endif
  165.     if (!l_prev)        /* first elt matched */
  166.       setelement(v_savedobjs, i, cdr(l_hbucket)); /* remove first elt */
  167.     else
  168.       rplacd(l_prev, cdr(l_hbucket)); /* remove elt pointed to by l_hbucket */
  169.   }
  170. #ifdef DEBUG
  171.   else
  172.     fprintf(stderr, "    Didn't remove any previous PixmapRef's\n");
  173. #endif
  174.  
  175.   /* save a new reference pixmap-obj under o_widget and lval_resname. */
  176.   xlstkcheck(3);
  177.   xlprotect(lval_pixmap);
  178.   xlsave(l_hbucket);
  179.   xlsave(refobj);
  180.  
  181.   refobj = new_pixrefobj();    /* create a "reference object" */
  182.   set_pixref_pixmap(refobj, lval_pixmap); /* point it to the pixmap it references */
  183.   set_pixref_widget(refobj, o_widget); /* set the widget referencing the pixmap */
  184.   set_pixref_resname(refobj, lval_resname); /* set the resource name on that widget */
  185.   l_hbucket = cons(refobj, getelement(v_savedobjs, i));    /* add reference obj to hashbucket */
  186.   setelement(v_savedobjs, i, l_hbucket); /* store it in in v_savedobjs so it won't get gc'd */
  187.   xlpopn(3);
  188. }
  189.  
  190.  
  191. /******************************************************************************
  192.  * This routine is called by the garbage collector on any unreferenced pixmap
  193.  * nodes. Note that XmDestroyPixmap() gets called on Pixmaps created by
  194.  * was created by calling primitive XM_GET_PIXMAP or via an indirect XtConvert()
  195.  * call in Wres_Append_LispArglist_To_XtArglist() (which also calls XmGetPixmap())
  196.  * This is done because we must ensure that we only call XmDestroyPixmap()
  197.  * once per invocation of XmGetPixmap() lest a pixmap be deallocated while it
  198.  * is still ref'd inside Motif.
  199.  ******************************************************************************/
  200. void Wpm_Decr_Refcount_Or_Free_Pixmap(p)
  201.      LVAL p;            /* XLTYPE_Pixmap */
  202. {
  203.   extern Screen*  screen;    /* global in winterp.c */
  204.  
  205. #ifdef DEBUG
  206.   fprintf(stderr, "\nWpm_Decr_Refcount_Or_Free_Pixmap(PixmapID=%lu).\n", (unsigned long) get_pixmap(p));
  207. #endif
  208.  
  209.   if (!XmDestroyPixmap(screen, get_pixmap(p)))
  210.     xlerror("Internal error in garbage collecting a pixmap -- XmDestroyPixmap() couldn't find pixmap in pixmap cache.",
  211.         p);
  212. }
  213.  
  214.  
  215. /******************************************************************************
  216.  * (XM_GET_PIXMAP <image-name> <foreground> <background>)
  217.  *
  218.  * <image-name> is a string representing a XImage that has been
  219.  * cached via XmInstallImage(). If such an XImage isn't found, then
  220.  * <image-name> is treated as the filename for a X10 or X11 bitmap file.
  221.  * You need to give a full pathname to the bitmap file, or alternately,
  222.  * you may set the environment variable XBMLANGPATH to the directories to
  223.  * be searched for bitmap files. XBMLANGPATH defaults to
  224.  * /usr/lib/X11/%L/bitmaps/%N/%B:/usr/lib/X11/%L/bitmaps/%B:/usr/lib/X11/bitmaps/%B:/usr/include/X11/bitmaps/%B
  225.  *
  226.  * In additions to images you have installed, Motif features 10 preinstalled
  227.  * images: "background", "25_foreground", "50_foreground", * "75_foreground",
  228.  * "vertical", "horizontal", "slant_right", "slant_left", "menu_cascade", "menu_checkmark".
  229.  *
  230.  * <forground> and <background> may be strings repreenting color names, or
  231.  * values of type XLTYPE_Pixel. (generated by X_ALLOC_COLOR, or
  232.  * via widget-method :get_values :XMN_FOREGROUND or :XMN_BACKGROUND.
  233.  *
  234.  * This function returns a value of type XLTYPE_Pixmap, which is suitable for
  235.  * passing on to any Pixmap-valued widget resource so as to display an image
  236.  * inside a widget.
  237.  *
  238.  * Note that there is no interface to XmDestroyPixmap()-- unreferenced pixmaps
  239.  * are automatically destroyed by winterp during garbage collection.
  240.  ******************************************************************************/
  241. LVAL Wpm_Prim_XmGetPixmap()
  242. {
  243.   extern Display* display;    /* global in winterp.c */
  244.   extern Screen*  screen;    /* global in winterp.c */
  245.   extern Colormap colormap;    /* global in winterp.c */
  246.   XColor        screenColor;
  247.   LVAL          str_image_name, lval_foreground, lval_background;
  248.   Pixel         foreground, background;
  249.   Pixmap        pixmap;
  250.  
  251.   str_image_name = xlgastring();
  252.  
  253.   lval_foreground = xlgetarg();
  254.   if (stringp(lval_foreground)) {
  255.     if (!XParseColor(display, colormap, (char*) getstring(lval_foreground), &screenColor))
  256.       xlerror("XParseColor() couldn't parse <foreground> color specification.", lval_foreground);
  257.     if (!XAllocColor(display, colormap, &screenColor))
  258.       xlerror("XAllocColor() couldn't allocate specified <foreground> color.", lval_foreground);
  259.     foreground = screenColor.pixel;
  260.   }
  261.   else if (pixel_p(lval_foreground))
  262.     foreground = get_pixel(lval_foreground);
  263.   else
  264.     xlerror("Bad type for <foreground> argument. Expected either a STRING or a PIXEL value.", lval_foreground);
  265.  
  266.   lval_background = xlgetarg();
  267.   if (stringp(lval_background)) {
  268.     if (!XParseColor(display, colormap, (char*) getstring(lval_background), &screenColor))
  269.       xlerror("XParseColor() couldn't parse <background> color specification.", lval_background);
  270.     if (!XAllocColor(display, colormap, &screenColor))
  271.       xlerror("XAllocColor() couldn't allocate specified <background> color.", lval_background);
  272.     background = screenColor.pixel;
  273.   }
  274.   else if (pixel_p(lval_background))
  275.     background = get_pixel(lval_background);
  276.   else
  277.     xlerror("Bad type for <background> argument. Expected either a STRING or a PIXEL value.", lval_background);
  278.  
  279.   xllastarg();
  280.  
  281.   pixmap = XmGetPixmap(screen, (char*) getstring(str_image_name), foreground, background);
  282.   if (pixmap == XmUNSPECIFIED_PIXMAP)
  283.     xlerror("XmGetPixmap() couldn't create a pixmap from given specification.", str_image_name);
  284.  
  285. #ifdef DEBUG
  286.   fprintf(stderr, "\nXmGetPixmap() returned PixmapID=%lu.\n", (unsigned long) pixmap);
  287. #endif
  288.  
  289.   return (cv_pixmap(pixmap));
  290. }
  291.  
  292.  
  293. /******************************************************************************
  294.  * (xm_install_image <ximage> <image_name>)
  295.  * where <ximage> is an XImage-type object as returned by XM_GET_XIMAGE_FROM_FILE
  296.  * <image_name> is a string, the name under which the XImage is cached
  297.  *
  298.  * returns T if success,  NIL if a NULL <ximage>, or duplicate <image_name>
  299.  * is given.
  300.  ******************************************************************************/
  301. LVAL Wpm_Prim_XmInstallImage()
  302. {
  303. #ifndef WINTERP_MOTIF_11    /* In 1.0 only, this is missing from Xm.h */
  304.   extern Boolean XmInstallImage(); /* lib/Xm/ImageCache.c */
  305. #endif                /* WINTERP_MOTIF_11 */
  306.   extern LVAL true;        /* xlglob.c */
  307.   XImage* image = get_ximage(xlga_ximage());
  308.   char*   name = (char*) getstring(xlgastring());
  309.   xllastarg();
  310.   if (XmInstallImage(image, name)) /* XmInstallImage() makes a local copy of <name>, the <name> argument can thus be safely freed via garbage collection */
  311.     return (true);
  312.   else
  313.     return (NIL);
  314. }
  315.  
  316.  
  317. /******************************************************************************
  318.  * (xm_uninstall_image <ximage>)
  319.  * where <ximage> is an XImage-type object as returned by XM_GET_XIMAGE_FROM_FILE
  320.  *
  321.  * returns T if success,  NIL if a NULL <ximage>, or <ximage> cannot be
  322.  * found in the image cache.
  323.  ******************************************************************************/
  324. LVAL Wpm_Prim_XmUninstallImage()
  325. {
  326. #ifndef WINTERP_MOTIF_11    /* In 1.0 only, this is missing from Xm.h */
  327.   extern Boolean XmUninstallImage(); /* lib/Xm/ImageCache.c */
  328. #endif                /* WINTERP_MOTIF_11 */
  329.   extern LVAL true;        /* xlglob.c */
  330.   XImage* image = get_ximage(xlga_ximage());
  331.   xllastarg();
  332.   if (XmUninstallImage(image))
  333.     return (true);
  334.   else
  335.     return (NIL);
  336. }
  337.  
  338.  
  339. /******************************************************************************
  340.  * (xm_get_ximage_from_file <filepath>)
  341.  * where <filepath> is the full name of the file containing the XImage data.
  342.  *
  343.  * returns an XImage object if an XImage was successfully retrieved from the
  344.  * given file; NIL if failure.
  345.  ******************************************************************************/
  346. LVAL Wpm_Prim_XmGetImageFromFile()
  347. {
  348.   XImage* image;
  349.   extern XImage* _XmGetImageFromFile(); /* lib/Xm/ReadImage.c */
  350.   char* filename = (char*) getstring(xlgastring());
  351.   xllastarg();
  352.   if (image = _XmGetImageFromFile(filename))
  353.     return (cv_ximage(image));
  354.   else
  355.     return (NIL);
  356. }
  357.  
  358.